package com.swak.rpc.config.boot;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.swak.utils.Functions;

/**
 * The shutdown hook thread to do the clean up stuff. This is a singleton in
 * order to ensure there is only one shutdown hook registered. to store the
 * shutdown hooks.
 */
public class ShutdownHook extends Thread {

	private static final Logger logger = LoggerFactory.getLogger(ShutdownHook.class);
	private static final ShutdownHook RPC_SHUTDOWN_HOOK = new ShutdownHook("RpcShutdownHook");

	public static ShutdownHook getInstance() {
		return RPC_SHUTDOWN_HOOK;
	}

	private final AtomicBoolean registered = new AtomicBoolean(false);
	private final AtomicBoolean destroyed = new AtomicBoolean(false);
	private final List<ShutdownHookCallback> callbacks = new LinkedList<>();

	private ShutdownHook(String name) {
		super(name);
	}

	@Override
	public void run() {
		if (logger.isInfoEnabled()) {
			logger.info("Run shutdown hook now.");
		}

		callbacks.forEach(callback -> Functions.ThrowableFunction.execute(callback::callback));
	}

	/**
	 * Register the ShutdownHook
	 */
	public void register() {
		if (registered.compareAndSet(false, true)) {
			destroyed.set(false);
			Runtime.getRuntime().addShutdownHook(RPC_SHUTDOWN_HOOK);
		}
	}

	/**
	 * Unregister the ShutdownHook
	 */
	public void unregister() {
		if (registered.compareAndSet(true, false)) {
			Runtime.getRuntime().removeShutdownHook(RPC_SHUTDOWN_HOOK);
		}
	}

	/**
	 * Add the ShutdownHookCallback
	 */
	public ShutdownHook addCallback(ShutdownHookCallback callback) {
		synchronized (this) {
			this.callbacks.add(callback);
		}
		return this;
	}
}
